package com.lagou.edu.factory;

import com.lagou.edu.annotation.*;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.reflections.Reflections;

import javax.servlet.annotation.WebListener;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
//@WebListener
public class BeanFactory  {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<String, Object> singleBeanPool = new HashMap<>();  // 存储对象
    private static Map<Class<?>, List<String>> classBean = new HashMap<>();  // 存储对象

//    private static String basePackage = "com.lagou.edu";
    private static String basePackage = "";
    //代理类 原始对象的前缀
    private static String PREFIX = "#";

    static {
        //加载xml ,获取扫描路径
        Document document = null;
        try {
            document = new SAXReader().read(BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml"));
            List<Element> list = document.getRootElement().selectNodes("component-scan");
            if (!list.isEmpty()){
                basePackage = list.get(0).attributeValue("basePackege");
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }

        // 加载包名下的所有类，获取注解
        Reflections reflections = new Reflections(basePackage);
        Set<Class<?>> types = reflections.getTypesAnnotatedWith(Service.class);
        for (Class<?> type : types) {
            Service service = type.getAnnotation(Service.class);
            addBean(service.value(),type);
        }
        types = reflections.getTypesAnnotatedWith(Repository.class);
        for (Class<?> type : types) {
            Repository service = type.getAnnotation(Repository.class);
            addBean(service.value(), type);
        }

        types = reflections.getTypesAnnotatedWith(Component.class);
        for (Class<?> type : types) {
            Component service = type.getAnnotation(Component.class);
            addBean(service.value(), type);
        }
        //属性注入
        for (Object obj : singleBeanPool.values()) {
            Class<?> aClass = obj.getClass();
            Field[] fields = aClass.getDeclaredFields();
            if (fields.length == 0) {
                continue;
            }
            for (Field field : fields) {
                field.setAccessible(true);
                Autowired annotation = field.getAnnotation(Autowired.class);
                if (annotation == null) {
                    continue;
                }
                try {
                    field.set(obj,getBean(field.getType()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        //处理创建代理类
        Map<String,Object> tranMap = new HashMap<>();
        for (Map.Entry<String, Object> entry : singleBeanPool.entrySet()) {
            Object obj = entry.getValue();
            Class<?> aClass = obj.getClass();
            //判断是否存在事务注解，如果有，则生成代理类
            Transactional transactional = aClass.getAnnotation(Transactional.class);
            if (transactional == null){
                Method[] methods = aClass.getDeclaredMethods();
                for (Method method : methods) {
                    transactional = method.getAnnotation(Transactional.class);
                    if (transactional != null){
                        break;
                    }
                }
            }
            if (transactional != null){
                //包含事务，使用动态代理
                Object proxy = null;
                ProxyFactory proxyFactory = (ProxyFactory)getBean(ProxyFactory.class);

                Class<?>[] interfaces = aClass.getInterfaces();
                if (interfaces == null  || interfaces.length == 0){
                    //实现接口，使用JDK动态代理
                    proxy = proxyFactory.getCglibProxy(obj);
                }else {
                    //未实现接口，使用cglib动态代理
                    proxy = proxyFactory.getJdkProxy(obj);
                }
                //使用新id保存原始对象
                String id = entry.getKey();
                tranMap.put(PREFIX+id, obj);
                //使用代理对象替换原始对象
                tranMap.put(id,proxy);
            }

        }
        //将代理对象和原始对象保存，代理对象替换原始对象的id
        singleBeanPool.putAll(tranMap);


    }



    private static void addBean(String id, Class<?> aClass) {
        Object o = null;
        try {
            o = aClass.getConstructor().newInstance();
            if (id == null || "".equals(id)) {
                //这里直接用类名做id
                id = aClass.getSimpleName();
            }
            Object put = singleBeanPool.put(id, o);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        //保存类与id的映射关系
        Class<?> superclass = aClass;
        while (!superclass.equals(Object.class)) {
            classBean.computeIfAbsent(superclass, s -> new ArrayList<>()).add(id);
            superclass = superclass.getSuperclass();
        }
        Class<?>[] interfaces = aClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            classBean.computeIfAbsent(anInterface, s -> new ArrayList<>()).add(id);
        }
    }



    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return singleBeanPool.get(id);
    }
    public static Object getBean(Class<?> tClass) {
        List<String> ids = classBean.get(tClass);
        if (ids != null && !ids.isEmpty()){
            return singleBeanPool.get(ids.get(0));
        }
        return null;
    }

}
